using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Reflection;
using System.Windows.Forms;

namespace VisualPureBasicIDE.FormDesigner.Implementations
{
    /// <summary>
    /// Summary description for ToolboxServiceImpl.
    /// </summary>
    public class ToolboxService : UserControl, IToolboxService
    {
        private IContainer _components;
        private ImageList _requiredImgList;
        private ImageList _treeViewImgList;
        public TreeView ToolboxView;

        public ToolboxService()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();

            //
            ToolboxView.ItemDrag += ToolboxViewItemDrag;
            ToolboxView.MouseDown += ToolboxViewMouseDown;
        }

        #region IToolboxService Members

        public void AddLinkedToolboxItem(ToolboxItem toolboxItem, string category, IDesignerHost host)
        {
            // TODO:  Add ToolboxServiceImpl.AddLinkedToolboxItem implementation
        }

        void IToolboxService.AddLinkedToolboxItem(ToolboxItem toolboxItem, IDesignerHost host)
        {
            // TODO:  Add ToolboxServiceImpl.System.Drawing.Design.IToolboxService.AddLinkedToolboxItem implementation
        }

        public ToolboxItem DeserializeToolboxItem(object serializedObject, IDesignerHost host)
        {
            // we know that we serialized the item in a dataobject, so 
            // get it out 
            return (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
        }

        ToolboxItem IToolboxService.DeserializeToolboxItem(object serializedObject)
        {
            return (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
        }

        public object SerializeToolboxItem(ToolboxItem toolboxItem)
        {
            // package the item in a dataobject
            var dataObj = new DataObject(toolboxItem);
            return dataObj;
        }

        public void RemoveToolboxItem(ToolboxItem toolboxItem, string category)
        {
            // we handle adding and removing of toolbox items ourself
        }

        void IToolboxService.RemoveToolboxItem(ToolboxItem toolboxItem)
        {
            // we handle adding and removing of toolbox items ourself
        }

        public bool SetCursor()
        {
            if (ToolboxView.SelectedNode == null)
            {
                return false;
            }
            if (ToolboxView.SelectedNode.Text == "Pointer")
            {
                return false;
            }
            return true;
        }

        public void AddToolboxItem(ToolboxItem toolboxItem, string category)
        {
            // we handle adding and removing of toolbox items ourself
        }

        void IToolboxService.AddToolboxItem(ToolboxItem toolboxItem)
        {
            // we handle adding and removing of toolbox items ourself
        }

        public void AddCreator(ToolboxItemCreatorCallback creator, string format, IDesignerHost host)
        {
            // we only deal with the standard win forms tools that we know of
        }

        void IToolboxService.AddCreator(ToolboxItemCreatorCallback creator, string format)
        {
            // we only deal with the standard win forms tools that we know of
        }

        public void RemoveCreator(string format, IDesignerHost host)
        {
            // we only deal with the standard win forms tools that we know of
        }

        void IToolboxService.RemoveCreator(string format)
        {
            // we only deal with the standard win forms tools that we know of
        }

        public ToolboxItemCollection GetToolboxItems(string category, IDesignerHost host)
        {
            return (this as IToolboxService).GetToolboxItems(category);
        }

        ToolboxItemCollection IToolboxService.GetToolboxItems(string category)
        {
            IList items = new ArrayList();
            // return all toolbox items
            if (ToolboxView != null && ToolboxView.Nodes != null && ToolboxView.Nodes.Count > 0)
            {
                // iterate over categories
                foreach (TreeNode nd in ToolboxView.Nodes)
                {
                    if (string.Compare(nd.Text, category, true) == 0)
                    {
                        foreach (TreeNode itemNode in nd.Nodes)
                        {
                            items.Add(itemNode.Tag);
                        }
                    }
                }
            }
            var obj = new ToolboxItem[items.Count];
            items.CopyTo(obj, 0);
            return new ToolboxItemCollection(obj);
        }

        ToolboxItemCollection IToolboxService.GetToolboxItems(IDesignerHost host)
        {
            IList items = new ArrayList();
            // return all toolbox items
            if (ToolboxView != null && ToolboxView.Nodes != null && ToolboxView.Nodes.Count > 0)
            {
                // iterate over categories
                foreach (TreeNode nd in ToolboxView.Nodes)
                {
                    foreach (TreeNode itemNode in nd.Nodes)
                    {
                        items.Add(itemNode.Tag);
                    }
                }
            }
            var obj = new ToolboxItem[items.Count];
            items.CopyTo(obj, 0);
            return new ToolboxItemCollection(obj);
        }

        ToolboxItemCollection IToolboxService.GetToolboxItems()
        {
            return (this as IToolboxService).GetToolboxItems((IDesignerHost) null);
        }

        public CategoryNameCollection CategoryNames
        {
            get
            {
                string[] names = null;
                if (ToolboxView != null && ToolboxView.Nodes != null && ToolboxView.Nodes.Count > 0)
                {
                    names = new string[ToolboxView.Nodes.Count];
                    var categoryCount = 0;
                    foreach (TreeNode nd in ToolboxView.Nodes)
                    {
                        names[categoryCount++] = nd.Text;
                    }
                }

                return new CategoryNameCollection(names);
            }
        }

        public ToolboxItem GetSelectedToolboxItem(IDesignerHost host)
        {
            ToolboxItem item = null;
            if (ToolboxView.SelectedNode != null && ToolboxView.SelectedNode.Tag != null &&
                ToolboxView.SelectedNode.Text != "Pointer")
            {
                item = ToolboxView.SelectedNode.Tag as ToolboxItem;
            }
            return item;
        }

        ToolboxItem IToolboxService.GetSelectedToolboxItem()
        {
            return (this as IToolboxService).GetSelectedToolboxItem(null);
        }

        public void SetSelectedToolboxItem(ToolboxItem toolboxItem)
        {
            // we handle this manually
        }

        public bool IsSupported(object serializedObject, ICollection filterAttributes)
        {
            return true;
        }

        bool IToolboxService.IsSupported(object serializedObject, IDesignerHost host)
        {
            return true;
        }

        public void SelectedToolboxItemUsed()
        {
            // after the user has selected a tool, then the framework calls
            // this method to select the pointer tool
            // get the category and select the first node (the pointer node)
            if (ToolboxView != null && ToolboxView.Nodes != null &&
                ToolboxView.Nodes.Count > 0 && ToolboxView.SelectedNode != null &&
                ToolboxView.SelectedNode.Parent != null &&
                string.Compare("Pointer", ToolboxView.SelectedNode.Text, true) != 0)
            {
                // get the parent node and select its pointer node
                var parentNode = ToolboxView.SelectedNode.Parent;
                foreach (TreeNode nd in parentNode.Nodes)
                {
                    if (string.Compare(nd.Text, "Pointer", true) == 0)
                    {
                        ToolboxView.SelectedNode = nd;
                        break;
                    }
                }
            }
        }

        public bool IsToolboxItem(object serializedObject, IDesignerHost host)
        {
            // TODO:  Add ToolboxServiceImpl.IsToolboxItem implementation
            return true;
        }

        bool IToolboxService.IsToolboxItem(object serializedObject)
        {
            // TODO:  Add ToolboxServiceImpl.System.Drawing.Design.IToolboxService.IsToolboxItem implementation
            return true;
        }

        public string SelectedCategory
        {
            get
            {
                string category = null;
                // get the selected node and find the category
                if (ToolboxView != null && ToolboxView.Nodes != null &&
                    ToolboxView.Nodes.Count > 0 && ToolboxView.SelectedNode != null &&
                    ToolboxView.SelectedNode.Tag is ToolboxItem)
                {
                    category = ToolboxView.SelectedNode.Parent.Text;
                }
                return category;
            }
            set
            {
                // TODO:  Add ToolboxServiceImpl.SelectedCategory setter implementation
            }
        }

        #endregion

        #region Helper Methods 

        /// <summary>
        /// loads a group of toolbox items into the under the given category
        /// </summary>
        /// <param name="cat"></param>
        private void LoadCategory(Category cat)
        {
            // if we have items in the category...
            if (cat != null && cat.FdToolboxItem != null && cat.FdToolboxItem.Length > 0)
            {
                // create a node for the category
                var catNode = new TreeNode(cat.DisplayName) {ImageIndex = 0, SelectedImageIndex = 0};
                // add this category to the tree
                ToolboxView.Nodes.Add(catNode);
                // every category gets the selection tool node
                AddSelectionNode(catNode);
                foreach (var item in cat.FdToolboxItem)
                {
                    LoadItem(item, catNode);
                }
            }
        }

        private static void AddSelectionNode(TreeNode cat)
        {
            if (cat != null)
            {
                var item = new ToolboxItem {DisplayName = "Pointer"};
                //				item.Bitmap=new Bitmap(this._treeViewImgList.Images[1]);
                var nd = new TreeNode(item.DisplayName) {ImageIndex = 1, SelectedImageIndex = 1, Tag = item};
                cat.Nodes.Add(nd);
            }
        }

        /// <summary>
        /// loads an item into the tree
        /// </summary>
        /// <param name="item"></param>
        /// <param name="cat"></param>
        private void LoadItem(FdToolboxItem item, TreeNode cat)
        {
            if (item != null && item.Type != null && cat != null)
            {
                // load the type
                var assemblyClass = item.Type.Split(new[] {','});
                // todo: check if the array actually contains two items
                var toolboxItemType = GetTypeFromLoadedAssembly(assemblyClass[0], assemblyClass[1]);
                //
                var toolItem = new ToolboxItem(toolboxItemType);
                // get the image for the item
                var img = GetItemImage(toolboxItemType);
                // create the node for it
                var nd = new TreeNode(toolItem.DisplayName);
                // add the item's bitmap to the image list
                if (img != null)
                {
                    // add it to the image list
                    _treeViewImgList.Images.Add(img);
                    // set the nodes image index
                    nd.ImageIndex = _treeViewImgList.Images.Count - 1;
                    // we have to set both the node's ImageIndex and
                    // SelectedImageIndex or we get some wierd behavior
                    // when we select nodes (the selected nodes image changes)...
                    nd.SelectedImageIndex = _treeViewImgList.Images.Count - 1;
                }
                nd.Tag = toolItem;
                // add this node to the category node
                cat.Nodes.Add(nd);
            }
        }

        /// <summary>
        /// finds the image associated with the type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static Image GetItemImage(Type type)
        {
            Image img = null;
            // get the AttributeCollection for the given type and
            // find the ToolboxBitmap attribute 
            var attrCol = TypeDescriptor.GetAttributes(type, true);
            var toolboxBitmapAttr = (ToolboxBitmapAttribute) attrCol[typeof (ToolboxBitmapAttribute)];
            if (toolboxBitmapAttr != null)
            {
                img = toolboxBitmapAttr.GetImage(type);
            }
            return img;
        }

        #endregion

        /// <summary>
        /// used to load the toolbox
        /// </summary>
        /// <param name="tools"></param>
        public void LoadToolbox(FdToolbox tools)
        {
            // clear out existing nodes and the imagelist associated with the tree
            ToolboxView.Nodes.Clear();
            _treeViewImgList.Dispose();
            _treeViewImgList = new ImageList(_components);
            // we have two images that we always use for category nodes
            // and the select tool node (pointer node).
            // add these in now
            _treeViewImgList.Images.Add(_requiredImgList.Images[0]);
            _treeViewImgList.Images.Add(_requiredImgList.Images[1]);
            // assign imagelist to the treeview
            ToolboxView.ImageList = _treeViewImgList;
            // if we have categories...
            if (tools != null && tools.FdToolboxCategories != null && tools.FdToolboxCategories.Length > 0)
            {
                foreach (var cat in tools.FdToolboxCategories)
                {
                    LoadCategory(cat);
                }
            }
        }

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_components != null)
                {
                    _components.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        private void ToolboxViewItemDrag(object sender, ItemDragEventArgs e)
        {
            DoDragDrop(e.Item, DragDropEffects.Copy);
        }

        private void ToolboxViewMouseDown(object sender, MouseEventArgs e)
        {
            // single click
            if (sender is TreeView && e.Clicks < 2)
            {
                var clickedNode = ToolboxView.GetNodeAt(e.X, e.Y);
                if (clickedNode != null)
                {
                    // select it
                    ToolboxView.SelectedNode = clickedNode;
                    // if the node is a top level category
                    // we don't do anything other than select it
                    if (clickedNode.Parent != null)
                    {
                        var item = clickedNode.Tag as ToolboxItem;
                        // if its not the pointer tool
                        if (item != null)
                            if (string.Compare(item.DisplayName, "Pointer", true) != 0)
                            {
                                // serialize the item and send it over
                                var dataObj = SerializeToolboxItem(item) as DataObject;
                                DoDragDrop(dataObj, DragDropEffects.Copy);
                            }
                    }
                }
            }
            else if (sender is TreeView && e.Clicks == 2)
            {
                // double click
            }
        }

        private static Type GetTypeFromLoadedAssembly(string classname, string assembly)
        {
            var loadedAsms = AppDomain.CurrentDomain.GetAssemblies();
            Type loadedType = null;

            Assembly foundAssembly = null;
            foreach (var asm in loadedAsms)
            {
                if (string.Compare(assembly, asm.GetName().Name, true) == 0)
                {
                    foundAssembly = asm;
                    break;
                }
            }
            if (foundAssembly != null)
            {
                loadedType = foundAssembly.GetType(classname);
            }
            else
            {
                // try to load it with a partial name
                if (assembly == "VisualPureBasicIDE.PureBasic.Gadgets")
                    assembly = "VisualPureBasicIDE.PureBasicBinding";
                var asm = Assembly.Load(assembly);
                if (asm != null)
                {
                    loadedType = asm.GetType(classname);
                }
            }
            return loadedType;
        }

        #region Component Designer generated code

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this._components = new System.ComponentModel.Container();
            var resources = new System.ComponentModel.ComponentResourceManager(typeof (ToolboxService));
            this._treeViewImgList = new System.Windows.Forms.ImageList(this._components);
            this.ToolboxView = new System.Windows.Forms.TreeView();
            this._requiredImgList = new System.Windows.Forms.ImageList(this._components);
            this.SuspendLayout();
            // 
            // _treeViewImgList
            // 
            this._treeViewImgList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
            this._treeViewImgList.ImageSize = new System.Drawing.Size(16, 16);
            this._treeViewImgList.TransparentColor = System.Drawing.Color.Transparent;
            // 
            // ToolboxView
            // 
            this.ToolboxView.BackColor = System.Drawing.Color.White;
            this.ToolboxView.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this.ToolboxView.Dock = System.Windows.Forms.DockStyle.Fill;
            this.ToolboxView.HideSelection = false;
            this.ToolboxView.Location = new System.Drawing.Point(0, 0);
            this.ToolboxView.Name = "ToolboxView";
            this.ToolboxView.Size = new System.Drawing.Size(304, 328);
            this.ToolboxView.TabIndex = 0;
            // 
            // requiredImgList
            // 
            this._requiredImgList.ImageStream =
                ((System.Windows.Forms.ImageListStreamer) (resources.GetObject("requiredImgList.ImageStream")));
            this._requiredImgList.TransparentColor = System.Drawing.Color.Transparent;
            this._requiredImgList.Images.SetKeyName(0, "");
            this._requiredImgList.Images.SetKeyName(1, "");
            // 
            // ToolboxServiceImpl
            // 
            this.Controls.Add(this.ToolboxView);
            this.Name = "ToolboxServiceImpl";
            this.Size = new System.Drawing.Size(304, 328);
            this.ResumeLayout(false);
        }

        #endregion
    }
}